using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Autofac;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using Refit;
using WebIM.API.Common.Exceptions;
using WebIM.API.Common.Models;
using WebIM.API.Schedules.Jobs;
using WebIM.API.Schedules.Quartz;
using WebIM.API.Services;
using WebIM.API.Thirdparty.Easemob;
using WebIM.API.Utilities.AutofacExtensions;

namespace WebIM.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Mongodb
            services.Configure<MyMongoDatabaseSettings>(Configuration.GetSection(nameof(MyMongoDatabaseSettings)));
            services.AddSingleton<IMyMongoDatabaseSettings>(sp =>
                sp.GetRequiredService<IOptions<MyMongoDatabaseSettings>>().Value);

            services.AddControllers(options =>
                options.Filters.Add(new MessageCodeExceptionFilter()));
            
            // 控制器由IoC容器创建
            services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebIM.API", Version = "v1" });
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
                c.IncludeXmlComments(Path.Combine(basePath ?? "/", "WebIM.API.xml"));
                c.DocInclusionPredicate((docName, description) => true);
            });

            #region quartz
            // 添加 Quartz 服务
            // services.AddSingleton<IJobFactory, RunnerJobFactory>();
            // services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
            // // 添加 Job
            // services.AddScoped<HelloWorldJob>();
            // services.AddSingleton(
            //     new JobSchedule(jobType: typeof(HelloWorldJob), cronExpression: "0/5 * * * * ?")
            // );
            // services.AddSingleton<QuartzJobRunner>();
            // services.AddHostedService<QuartzHostedService>();
            #endregion

            #region Refit 环信接口
            // 配置
            services.Configure<EasemobSettings>(Configuration.GetSection(nameof(EasemobSettings)));
            services.AddSingleton<IEasemobSettings>(sp =>
                sp.GetRequiredService<IOptions<EasemobSettings>>().Value);
            
            // Refit Client
            services.AddRefitClient<IEasemobApi>()
                .ConfigureHttpClient(c => c.BaseAddress = new Uri("http://a1.easemob.com"))
                .AddHttpMessageHandler<EasemobAuthHeaderHandler>();
            #endregion
           
            
            services.AddHttpClient();
        }
        
        /// <summary>
        /// Autofac 自动调用该方法
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            /* 注册自定义Filter
            builder.RegisterType(typeof(RequirePermissionAttribute))
                .PropertiesAutowired(new AutowiredPropertySelector());
            */
            
            // Autofac 几种生命周期类型 https://www.cnblogs.com/stulzq/p/8547277.html
            
            // 环信服务
            builder.RegisterType<EasemobAuthTokenStoreService>().As<IEasemobAuthTokenStore>().SingleInstance();
            builder.RegisterType(typeof(EasemobAuthHeaderHandler)); // = .InstancePerDependency();
            
            #region 注册所有控制器的关系
            var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
                .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
            builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(new AutowiredPropertySelector());
            #endregion
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebIM.API v1"));
            }

            app.UseRouting();

            app.UseAuthorization();

            // 默认异常处理器
            app.UseExceptionHandler(builder =>
            {
                builder.Run(async context =>
                {
                    context.Response.StatusCode = 200;
                    context.Response.ContentType = "application/json";

                    var exception = context.Features.Get<IExceptionHandlerFeature>();
                    if (exception is not null)
                    {
                        MessageCodeResult<object> resp = new MessageCodeResult<object>
                        {
                            Code = 500, Message = exception.Error.Message
                        };
                        var output = JsonSerializer.Serialize(resp);
                        await context.Response.WriteAsync(output).ConfigureAwait(false);
                    }
                });
            });
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
